<?php

namespace app\api\controller;

use app\common\controller\Api;
use think\Request;
use Volc\Service\Ark;

/**
 * 豆包AI流式输出接口
 */
class Doubao extends Api
{
    protected $noNeedLogin = ['*'];
    protected $noNeedRight = ['*'];

    private $config;
    private $apiKey;
    private $streamMode = 'char'; // 默认流式模式

    public function _initialize()
    {
        parent::_initialize();
        $this->config = config('doubao');
        $this->apiKey = $this->config['api_key'];
    }

    /**
     * 豆包AI流式对话接口
     * GET/POST /api/doubao/index
     * 参数: question=你好&temperature=0.7&max_tokens=4000&stream_mode=char
     * stream_mode: char(逐字符) | word(逐词) | chunk(分块) | complete(完整)
     */
    public function index(Request $request)
    {
        try {
            // 获取参数
            $question = trim($request->param('question', '你好，我是豆包AI'));
            $temperature = (float)$request->param('temperature', 0.7);
            $maxTokens = (int)$request->param('max_tokens', 4000);
            $model = $request->param('model', $this->config['default_model']);
            $streamMode = $request->param('stream_mode', 'char');
            
            // 参数验证
            if (empty($question)) {
                $this->streamError('问题内容不能为空');
                return;
            }
            
            if (mb_strlen($question, 'UTF-8') > 10000) {
                $this->streamError('问题内容过长，请控制在10000字符以内');
                return;
            }
            
            if ($temperature < 0 || $temperature > 2) {
                $this->streamError('temperature参数必须在0-2之间');
                return;
            }
            
            if ($maxTokens < 1 || $maxTokens > 8000) {
                $this->streamError('max_tokens参数必须在1-8000之间');
                return;
            }
            
            // 验证流式模式
            $validModes = ['char', 'word', 'chunk', 'complete'];
            if (!in_array($streamMode, $validModes)) {
                $this->streamError('stream_mode参数必须是: ' . implode(', ', $validModes));
                return;
            }
            
            // 设置流式模式
            $this->streamMode = $streamMode;
            
            // 启动流式对话
            $this->startStreamChat($question, $temperature, $maxTokens, $model);
            
        } catch (\Exception $e) {
            error_log('豆包AI流式聊天错误: ' . $e->getMessage());
            $this->streamError('系统错误: ' . $e->getMessage());
        }
    }

    /**
     * 启动流式对话
     */
    private function startStreamChat($question, $temperature, $maxTokens, $model)
    {
        // 设置流式响应头
        $this->setOptimizedHeaders();
        
        try {
            $this->sendEvent('start', '开始豆包AI对话...');
            // 使用火山引擎SDK调用豆包AI
            $client = Ark::getInstance();
            // 设置认证信息（使用Bearer Token方式）
            $client->setAccessKey('AKLTNzliZTJiNGRiZmE4NGNhN2E4NzI1MWI1OGE1NDFlY2M'); // 使用环境变量或配置文件中的AK/SK
            $client->setSecretKey('WkdaaE1URTNNemt5TkRCak5HWmxaR0pqTXpFMU5qWmlOMkV6TlRsbU4yVQ==');
            // 构建请求数据
            $requestData = [
                'model' => $model,
                'messages' => [
                    [
                        'role' => 'user',
                        'content' => $question
                    ]
                ],
                'temperature' => $temperature,
                'max_tokens' => $maxTokens,
                'stream' => true,
                'top_p' => 0.9
            ];
            
            // 验证请求数据
            $this->validateRequestData($requestData);
            
            // 执行流式请求
            $this->executeRequestWithSDK($requestData);
            
        } catch (\Exception $e) {
            error_log('流式对话启动错误: ' . $e->getMessage());
            $this->sendEvent('error', '启动对话失败: ' . $e->getMessage());
        }
        
        exit;
    }

    /**
     * 验证请求数据
     */
    private function validateRequestData($requestData)
    {
        if (empty($requestData['messages'])) {
            throw new \Exception('消息内容不能为空');
        }
        
        if (empty($requestData['model'])) {
            throw new \Exception('模型参数不能为空');
        }
        
        // 检查消息长度
        $totalLength = 0;
        foreach ($requestData['messages'] as $message) {
            if (isset($message['content']) && is_string($message['content'])) {
                $totalLength += mb_strlen($message['content'], 'UTF-8');
            }
        }
        
        if ($totalLength > 50000) {
            throw new \Exception('消息内容过长，请减少内容后重试');
        }
    }

    /**
     * 使用SDK执行流式API请求
     */
    private function executeRequestWithSDK($data)
    {
        try {
            // 记录请求开始
            error_log('豆包API请求开始: ' . json_encode([
                'model' => $data['model'] ?? 'unknown',
                'stream_mode' => $this->streamMode
            ]));
            
            // 使用火山引擎SDK调用豆包AI
            $client = Ark::getInstance();
            
            // 使用Bearer Token认证方式
            $headers = [
                'Authorization' => 'Bearer ' . $this->apiKey,
                'Content-Type' => 'application/json',
                'Accept' => 'text/event-stream'
            ];
            
            // 构建请求配置
            $config = [
                'headers' => $headers,
                'json' => $data
            ];
            
            // 使用SDK的流式聊天完成方法
            $response = $client->streamChatCompletions($data, function($line) {
                // 处理每一行数据
                $this->processStreamLine($line);
            });
            
        } catch (\Exception $e) {
            error_log('豆包API请求错误: ' . $e->getMessage());
            $this->sendEvent('error', '连接错误: ' . $e->getMessage());
        }
    }

    /**
     * 处理流式响应的每一行数据
     */
    private function processStreamLine($line)
    {
        try {
            // 处理SSE数据行
            if (strpos($line, 'data: ') === 0) {
                $json = substr($line, 6);
                
                // 检查流结束标记
                if ($json === '[DONE]') {
                    error_log('豆包API流式结束标记接收');
                    $this->sendEvent('end', '对话完成');
                    return;
                }
                
                // 解析JSON数据
                $responseData = json_decode($json, true);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    error_log('豆包API JSON解析错误: ' . json_last_error_msg() . ', 数据: ' . $json);
                    return;
                }
                
                // 处理豆包API响应
                if (isset($responseData['choices'][0]['delta']['content'])) {
                    $content = $responseData['choices'][0]['delta']['content'];
                    
                    if (!empty($content)) {
                        // 清理内容
                        $cleanContent = $this->cleanContent($content);
                        if (!empty($cleanContent)) {
                            // 根据流式模式发送内容
                            $this->sendContentByMode($cleanContent);
                        }
                    }
                }
                
                // 处理错误情况
                if (isset($responseData['error'])) {
                    error_log('豆包API返回错误: ' . json_encode($responseData['error']));
                    $this->sendEvent('error', '豆包API错误: ' . $responseData['error']['message']);
                }
            }
        } catch (\Exception $e) {
            error_log('处理流式行数据错误: ' . $e->getMessage());
        }
    }

    /**
     * 根据流式模式发送内容
     */
    private function sendContentByMode($content)
    {
        switch ($this->streamMode) {
            case 'char':
                $this->sendContentCharByChar($content);
                break;
            case 'word':
                $this->sendContentWordByWord($content);
                break;
            case 'chunk':
                $this->sendContentAsChunk($content);
                break;
            case 'complete':
                // complete模式不在这里发送，等待最后一次性发送
                break;
            default:
                $this->sendContentCharByChar($content);
        }
    }

    /**
     * 逐字符发送内容
     */
    private function sendContentCharByChar($content)
    {
        $chars = preg_split('//u', $content, -1, PREG_SPLIT_NO_EMPTY);
        
        foreach ($chars as $char) {
            if (!empty($char)) {
                $this->sendEvent('content', $char);
                
                // 根据字符类型调整延迟
                if (preg_match('/[\x{4e00}-\x{9fff}]/u', $char)) {
                    usleep(50000); // 中文字符 50ms
                } else {
                    usleep(30000); // 英文字符 30ms
                }
            }
        }
    }

    /**
     * 逐词发送内容
     */
    private function sendContentWordByWord($content)
    {
        $words = $this->splitContentIntoWords($content);
        
        foreach ($words as $word) {
            if (!empty($word)) {
                $this->sendEvent('content', $word);
                
                // 根据词语类型调整延迟
                if (preg_match('/[\x{4e00}-\x{9fff}]/u', $word)) {
                    usleep(80000); // 中文词 80ms
                } else {
                    usleep(60000); // 英文词 60ms
                }
            }
        }
    }

    /**
     * 分块发送内容
     */
    private function sendContentAsChunk($content)
    {
        $this->sendEvent('content', $content);
        usleep(100000); // 100ms延迟
    }

    /**
     * 将内容分割成词语
     */
    private function splitContentIntoWords($content)
    {
        // 使用正则表达式分割中英文词语
        preg_match_all('/[\x{4e00}-\x{9fff}]+|[a-zA-Z]+|\d+|[^\x{4e00}-\x{9fff}a-zA-Z\d\s]+|\s+/u', $content, $matches);
        return $matches[0] ?? [];
    }

    /**
     * 清理内容
     */
    private function cleanContent($content)
    {
        // 移除控制字符，但保留换行符和制表符
        $content = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/', '', $content);
        return $content;
    }

    /**
     * 设置优化的响应头
     */
    private function setOptimizedHeaders()
    {
        // 禁用输出缓冲
        if (ob_get_level()) {
            ob_end_clean();
        }
        
        // 设置PHP配置
        ini_set('output_buffering', 'off');
        ini_set('zlib.output_compression', 'off');
        
        // 基本SSE头
        header('Content-Type: text/event-stream');
        header('Cache-Control: no-cache');
        header('Connection: keep-alive');
        header('Access-Control-Allow-Origin: *');
        header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type, Authorization, X-Requested-With');
        header('X-Accel-Buffering: no'); // 禁用Nginx缓冲
        
        // 无限制执行时间
        set_time_limit(0);
        ignore_user_abort(false);
        
        // 立即刷新输出
        if (function_exists('fastcgi_finish_request')) {
            fastcgi_finish_request();
        }
        
        // 发送初始数据以建立连接
        echo "data: " . json_encode(['type' => 'init', 'message' => '连接已建立']) . "\n\n";
        
        // 启用自动刷新
        if (function_exists('apache_setenv')) {
            apache_setenv('no-gzip', '1');
        }
        
        flush();
    }

    /**
     * 发送SSE事件
     */
    private function sendEvent($type, $data)
    {
        $eventData = [
            'type' => $type,
            'data' => $data,
            'timestamp' => microtime(true)
        ];
        
        // 构建SSE格式
        $sseData = "data: " . json_encode($eventData) . "\n\n";
        
        // 发送数据
        echo $sseData;
        
        // 强制刷新输出缓冲区
        if (ob_get_level()) {
            ob_flush();
        }
        flush();
        
        // 调试记录（仅在开发环境）
        if (defined('APP_DEBUG') && APP_DEBUG) {
            error_log("SSE发送: {$type} - " . substr($data, 0, 100));
        }
    }

    /**
     * 获取HTTP错误消息
     */
    private function getHttpErrorMessage($httpCode)
    {
        $errorMessages = [
            400 => '请求参数错误',
            401 => 'API密钥无效或过期',
            403 => '没有访问权限',
            404 => 'API地址不存在',
            429 => '请求过于频繁，请稍后再试',
            500 => '服务器内部错误',
            502 => '网关错误',
            503 => '服务不可用',
            504 => '网关超时'
        ];
        
        return $errorMessages[$httpCode] ?? '未知HTTP错误';
    }

    /**
     * 发送错误SSE事件并结束连接
     */
    private function streamError($message, $code = 500)
    {
        $this->sendEvent('error', [
            'message' => $message,
            'code' => $code,
            'timestamp' => date('Y-m-d H:i:s')
        ]);
        
        $this->sendEvent('end', '连接因错误而结束');
        
        // 记录错误日志
        error_log("豆包API流式错误 [{$code}]: {$message}");
        
        exit;
    }
}


public function startStreamChat($messages, $model) {
    $url = "https://ark.cn-beijing.volces.com/api/v3/chat/completions";
    $apiKey = getenv('DOUBAO_API_KEY'); // 从环境变量获取 API 密钥

    $client = new \GuzzleHttp\Client();
    $headers = [
        'Content-Type' => 'application/json',
        'Authorization' => "Bearer $apiKey",
    ];
    $body = [
        'messages' => $messages,
        'model' => $model,
    ];

    try {
        $response = $client->post($url, [
            'headers' => $headers,
            'json' => $body,
        ]);

        $responseData = json_decode($response->getBody(), true);
        // 根据需要处理返回数据
        return $responseData;
    } catch (\Exception $e) {
        // 错误处理
        return ['error' => $e->getMessage()];
    }
}